/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2007 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
aLONG with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Description:
============
*/
#pragma once
namespace NContactDetailsPS {

typedef struct TPropSheet	PS, *LPPS;

class CPsHdr;
class CPsTree;

/***********************************************************************************************************
 * Tree item
 ***********************************************************************************************************/

class CPsTreeItem 
{
	INT				_idDlg;			// resource id of the property page
	LPDLGTEMPLATE	_pTemplate;		// locked template for the property page
	HINSTANCE		_hInst;			// instance handle of the owning plugin dll
	DLGPROC			_pfnDlgProc;	// dialog procedure for the property page
	HWND			_hWnd;			// window handle for the property page if shown jet
	DWORD			_dwFlags;		// some flags
	INT				_iPosition;		// initiating position if custom (used for sorting)

	HANDLE			_hContact;		// contact the page is accociated with (may be a meta subcontact if details dialog is shown for a meta contact)
	LPCSTR			_pszProto;		// protocol the page is accociated with (is the contact's protocol if _hContact is not NULL)
	LPCSTR			_pszPrefix;		// pointer to the dialog owning contact's protocol

	HTREEITEM		_hItem;			// handle to the treeview item if visible (NULL if this item is hidden)
	INT				_iParent;		// index of the owning tree item
	INT				_iImage;		// index of treeview item's image
	BYTE			_bState;		// initial state of this treeitem
	LPSTR			_pszName;		// original name, given by plugin (not customized)
	LPTSTR			_ptszLabel;		// string to setting in db holding information about this treeitem

	LPCSTR	GlobalName();

	INT		Name(LPTSTR pszTitle, const BOOLEAN bIsUnicode);
	INT		ItemLabel(const BOOLEAN bReadDBValue);

	HICON	ProtoIcon();
	INT		Icon(HIMAGELIST hIml, OPTIONSDIALOGPAGE *odp, BOOLEAN bInitIconsOnly);

public:
	CPsTreeItem();
	~CPsTreeItem();
	INT		Create(CPsHdr* pPsh, OPTIONSDIALOGPAGE *odp);

	__inline LPSTR		Name() const						{ return _pszName;		};
	__inline LPCSTR		Proto() const						{ return _pszProto;		};
	__inline LPTSTR		Label() const						{ return _ptszLabel;	};
	VOID				Rename(const LPTSTR pszLabel);

	__inline HANDLE		hContact() const					{ return _hContact;		};

	__inline HWND		Wnd() const							{ return _hWnd;			};
	__inline INT		DlgId() const						{ return _idDlg;		};
	__inline HINSTANCE	Inst() const						{ return _hInst;		};

	__inline INT		Image() const						{ return _iImage;		};
	__inline INT		Pos() const							{ return _iPosition;	};
	__inline BYTE		State() const						{ return _bState;		};
	__inline HTREEITEM	Hti() const							{ return _hItem;		};
	__inline VOID		Hti(HTREEITEM hti)					{ _hItem = hti;			};
	__inline INT		Parent() const						{ return _iParent;		};
	__inline VOID		Parent(const INT iParent)			{ _iParent = iParent;	};

	__inline DWORD		Flags() const						{ return _dwFlags;		};
	__inline VOID		Flags(DWORD dwFlags)				{ _dwFlags = dwFlags;	};
	__inline VOID		AddFlags(DWORD dwFlags)				{ _dwFlags |= dwFlags;	};
	__inline VOID		RemoveFlags(DWORD dwFlags)			{ _dwFlags &= ~dwFlags;	};

	BOOLEAN				HasName(const LPCSTR pszName) const;

	
	LPCSTR	PropertyKey(LPCSTR pszProperty);
	LPCSTR	GlobalPropertyKey(LPCSTR pszProperty);
	LPCSTR	IconKey();

	LPSTR	ParentItemName();
	HWND	CreateWnd(LPPS pPs);
	
	WORD	DBSaveItemState(LPCSTR pszGroup, INT iItemPosition, UINT iState, DWORD dwFlags);

	// notification handlers
	VOID OnInfoChanged();
	VOID OnPageIconsChanged();
	VOID OnIconsChanged(CPsTree *pTree);

};


/***********************************************************************************************************
 * Tree control
 ***********************************************************************************************************/

#define MAX_TINAME			64

// internal setting strings
#define SET_LASTITEM		"LastItem"
#define SET_ITEM_LABEL		"label"
#define SET_ITEM_GROUP		"group"
#define SET_ITEM_STATE		"state"
#define SET_ITEM_POS		"pos"
#define TREE_ROOTITEM		"<ROOT>"

// treeview states
#define DBTVIS_INVISIBLE	0
#define DBTVIS_NORMAL		1
#define DBTVIS_EXPANDED		2

// flags for the treeview
#define PSTVF_SORTTREE			0x00010000
#define	PSTVF_GROUPS			0x00020000
#define	PSTVF_LABEL_CHANGED		0x00040000
#define	PSTVF_POS_CHANGED		0x00080000
#define	PSTVF_STATE_CHANGED		0x00100000
#define	PSTVF_INITICONS			0x00200000		// flag that indicates the AddPage handler to load only basic stuff

class CPsTree {
	HWND			_hWndTree;
	HIMAGELIST		_hImages;
	CPsTreeItem**	_pItems;
	INT				_curItem;
	INT				_numItems;
	DWORD			_dwFlags;
	HWND			_hLabelEdit;
	HTREEITEM		_hDragItem;
	BOOLEAN			_isDragging;
	LPPS			_pPs;

	WORD	SaveItemsState(LPCSTR pszGroup, HTREEITEM hRootItem, INT& iItem);

public:
	CPsTree(LPPS pPs);
	~CPsTree();
	
	__inline VOID		BeginDrag(HTREEITEM hDragItem)		{ _isDragging = TRUE; _hDragItem = hDragItem; };
	__inline VOID		EndDrag()							{ _isDragging = FALSE; _hDragItem = NULL; };
	__inline BOOLEAN	IsDragging() const					{ return _isDragging; };
	__inline HTREEITEM	DragItem() const					{ return _hDragItem; };

	__inline DWORD		Flags() const						{ return _dwFlags; };
	__inline VOID		Flags(DWORD dwFlags)				{ _dwFlags = dwFlags; };
	__inline VOID		AddFlags(DWORD dwFlags)				{ _dwFlags |= dwFlags; };
	__inline VOID		RemoveFlags(DWORD dwFlags)			{ _dwFlags &= ~dwFlags; };

	__inline INT		NumItems() const					{ return _numItems; };
	__inline HWND		Window() const						{ return _hWndTree; };
	__inline HIMAGELIST	ImageList() const					{ return _hImages; };
	__inline BOOLEAN	IsIndexValid(const INT index) const	{ return (index >= 0 && index < _numItems); };
	
	__inline CPsTreeItem*	TreeItem(INT index) const		{ return (IsIndexValid(index) ? _pItems[index] : NULL); };
	__inline HTREEITEM		TreeItemHandle(INT index) const	{ return (IsIndexValid(index) ? _pItems[index]->Hti() : NULL); };

	__inline INT			CurrentItemIndex() const		{ return _curItem; };
	__inline CPsTreeItem*	CurrentItem() const				{ return TreeItem(CurrentItemIndex()); };

	INT			AddDummyItem(LPCSTR pszGroup);
	BOOLEAN		Create(HWND hWndTree, CPsHdr* pPsh);
	BOOLEAN		InitTreeItems(LPWORD needWidth);
	
	VOID		HideItem(const INT iPageIndex);
	HTREEITEM	ShowItem(const INT iPageIndex, LPWORD needWidth);

	HTREEITEM	MoveItem(HTREEITEM hItem, HTREEITEM hInsertAfter, BOOLEAN bAsChild = FALSE);
	VOID		SaveState();
	VOID		DBResetState();

	INT			FindItemIndexByHandle(HTREEITEM hItem);
	INT			FindItemIndexByName(LPCSTR pszName);

	CPsTreeItem*	FindItemByName(LPCSTR pszName);
	CPsTreeItem*	FindItemByHandle(HTREEITEM hItem);
	HTREEITEM		FindItemHandleByName(LPCSTR pszName);
	CPsTreeItem*	FindItemByResource(HINSTANCE hInst, INT idDlg);

	INT			BeginLabelEdit(HTREEITEM hItem);
	INT			EndLabelEdit(const BOOLEAN bSave);
	VOID		PopupMenu();
	
	VOID		OnIconsChanged();
	BOOLEAN		OnInfoChanged();
	BOOLEAN		OnSelChanging();
	VOID		OnSelChanged(LPNMTREEVIEW lpnmtv);
	VOID		OnCancel();
	INT			OnApply();
};

/***********************************************************************************************************
 * common stuff
 ***********************************************************************************************************/

// internal flags for the PSP structure
#define		PSPF_CHANGED			4096
#define		PSPF_PROTOPREPENDED		8192		// the first token of the title is the protocol

#define PSF_PROTOPAGESONLY			64			// load only contact's protocol pages
#define PSF_PROTOPAGESONLY_INIT		128

class CPsUpload;

class CPsHdr {
public:
	DWORD			_dwSize;
	HANDLE			_hContact;
	LPCSTR			_pszProto;
	LPCSTR			_pszPrefix;		// name prefix for treeitem settings
	CPsTreeItem**	_pPages;
	WORD			_numPages;
	DWORD			_dwFlags;
	HIMAGELIST		_hImages;

	// list of to skipp items when adding metasubcontacts pages
	struct {
		std::list<string>  A;
		std::list<wstring> W;
	} _ignore;

	CPsHdr() {
		_dwSize = sizeof(*this);
		_hContact = NULL;
		_pszProto = NULL;
		_pszPrefix = NULL;
		_pPages = NULL;
		_numPages = 0;
		_dwFlags = 0;
		_hImages = NULL;
	};
};

struct TPropSheet {

	// dialogs owner
	HANDLE		hContact;
	CHAR		szBaseProto[MAXMODULELABELLENGTH];


	HANDLE		hProtoAckEvent;		// eventhook for protocol acks
	HANDLE		hSettingChanged;	// eventhook searching for changed contact information
	HANDLE		hIconsChanged;		// eventhook for changed icons in icolib
	HFONT		hBoldFont;
	RECT		rcDisplay;
	BYTE		updateAnimFrame;
	CHAR		szUpdating[64];
	LPINT		infosUpdated;
	DWORD		dwFlags;
	
	// controls
	HWND		hDlg;
	CPsTree		*pTree;
	CPsUpload	*pUpload;
};

VOID	InitTreeIcons();
VOID	LoadModule();
VOID	UnLoadModule();

} // namespace NContactDetailsPS